package com.zeyu.framework.modules.common;

import com.google.common.collect.Lists;
import com.zeyu.framework.core.security.SystemAuthorizingRealm;
import com.zeyu.framework.core.security.UsernamePasswordToken;
import com.zeyu.framework.core.security.utils.SecurityUtils;
import com.zeyu.framework.core.web.BaseController;
import com.zeyu.framework.core.web.Result;
import com.zeyu.framework.core.web.servlet.Servlets;
import com.zeyu.framework.modules.common.openid.OpenIdManager;
import com.zeyu.framework.modules.common.openid.provider.WeiXinAuthProvider;
import com.zeyu.framework.modules.sys.dao.UserLoginProviderDao;
import com.zeyu.framework.modules.sys.entity.Office;
import com.zeyu.framework.modules.sys.entity.Role;
import com.zeyu.framework.modules.sys.entity.User;
import com.zeyu.framework.modules.sys.entity.UserLoginProvider;
import com.zeyu.framework.modules.sys.service.SystemService;
import com.zeyu.framework.modules.sys.utils.UserUtils;
import com.zeyu.framework.utils.CacheUtils;
import com.zeyu.framework.utils.CookieUtils;
import com.zeyu.framework.utils.IdGen;
import com.zeyu.framework.utils.RegexUtils;
import com.zeyu.framework.utils.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.shiro.subject.Subject;
import org.brickred.socialauth.AuthProvider;
import org.brickred.socialauth.Profile;
import org.brickred.socialauth.SocialAuthConfig;
import org.brickred.socialauth.SocialAuthManager;
import org.brickred.socialauth.util.SocialAuthUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 公共Controller,这个controller里的请求可以无验证访问
 */
@Controller
@RequestMapping(value = "/common")
public class CommonController extends BaseController {

    // ================================================================
    // Constants
    // ================================================================

    // ================================================================
    // Fields
    // ================================================================

    @Autowired
    private SystemService systemService;

    @Autowired
    private UserLoginProviderDao userLoginProviderDao;

    // ================================================================
    // Constructors
    // ================================================================

    // ================================================================
    // Methods from/for super Interfaces or SuperClass
    // ================================================================

    // ================================================================
    // Public or Protected Methods
    // ================================================================

    /**
     * session过期后shiro的处理页面
     */
    @RequestMapping(value = "sessionTimeout")
    public String sessionTimeout() {
        return "error/sessionTimeout";
    }

    /**
     * openid登录前请求处理流程,获取url,重定向到认证页面
     */
    @RequestMapping(value = "/openid/beforeLogin")
    public String beforeLogin(String type, HttpServletResponse response) {
        try {
            if (type != null) {
                // 微信只能扫描二维码登录,使用单独的流程
                if (StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_QQ) ||
                        StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_WEIBO) ||
                        StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_GOOGLE) ||
                        StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_YAHOO) ||
                        StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_HOTMAIL) ||
                        StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_GITHUB) ||
                        StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_OSCHINA)) {

                    // 认证管理器
                    SocialAuthManager manager = new SocialAuthManager();
                    manager.setSocialAuthConfig(OpenIdManager.getConfig());
                    // get Provider URL to which you should redirect for authentication.
                    String authUrl = manager.getAuthenticationUrl(type, OpenIdManager.AFTER_BIND_URL);
                    // Store in session
                    String socialauthId = RandomStringUtils.randomAlphanumeric(20);
                    // 清除旧的cookie
                    CookieUtils.setCookie(response, OpenIdManager.SOCIAL_AUTH_KEY, "", 0);
                    // 设置新的cookie
                    CookieUtils.setCookie(response, OpenIdManager.SOCIAL_AUTH_KEY, socialauthId, -1);
                    // 放入cache
                    CacheUtils.put(OpenIdManager.SOCIAL_AUTH_CACHE, socialauthId, manager);

                    // 通过response跳转到认证网站
                    response.setContentType("text/html");
                    if (!CHARSET_UTF_8.equalsIgnoreCase(response.getCharacterEncoding()))
                        response.setCharacterEncoding(CHARSET_UTF_8);
                    String html = "<p>Redirecting...</p><script type='text/javascript'>location.href='"
                            + authUrl + "';</script>";
                    try {
                        response.getWriter().print(html);
                        response.getWriter().flush();
                        response.getWriter().close();
                    } catch (IOException e) {
                        logger.error("redirect auth page error: " + e.getMessage());
                    }
                } else if (StringUtils.equalsIgnoreCase(type, OpenIdManager.PROVIDER_WEIXIN)) {
                    // 简单模拟获取token,无更高权限
                    SocialAuthConfig config = OpenIdManager.getConfig();
                    String consumer_key = config.getApplicationProperties()
                            .getProperty(OpenIdManager.PROVIDER_WEIXIN + ".consumer_key");
                    String consumer_secret = config.getApplicationProperties()
                            .getProperty(OpenIdManager.PROVIDER_WEIXIN + ".consumer_secret");
                    WeiXinAuthProvider weiXinAuthProvider = new WeiXinAuthProvider();
                    weiXinAuthProvider.auth("", consumer_key, consumer_secret);
                }
            }
        } catch (Exception e) {
            logger.error("openid login error: ", e);
        }
        return "";
    }

    /**
     * openid登录前请求处理流程,获取url,重定向到认证页面
     */
    @RequestMapping(value = "/openid/afterLogin")
    public String afterLogin(HttpServletRequest request, HttpServletResponse response) {

        // 登录验证
        SystemAuthorizingRealm.Principal principal = SecurityUtils.getPrincipal();

        // 如果已经登录，则跳转到管理首页
        if (principal != null) {
            return "redirect:/common/sessionTimeout";
        }

        // get the auth provider manager from cookie and cache
        String socialauthId = CookieUtils.getCookie(request, OpenIdManager.SOCIAL_AUTH_KEY);
        try {
            if (socialauthId != null) {
                SocialAuthManager manager = (SocialAuthManager) CacheUtils
                        .get(OpenIdManager.SOCIAL_AUTH_CACHE, socialauthId);
                if (manager == null) {
                    logger.info(Servlets.getRemoteAddress(request)
                            + " ---------------> SocialAuthManager is Null "
                            + request.getHeader("user-agent"));
                    return "redirect:/common/sessionTimeout";
                }
                Profile profile;
                AuthProvider provider;
                try {
                    // call connect method of manager which returns the provider object.
                    // Pass request parameter map while calling connect method.
                    provider = manager.connect(SocialAuthUtil.getRequestParametersMap(request));
                    // get profile
                    profile = provider.getUserProfile();
                } catch (Exception e) {
                    logger.error("Provider connection error: ", e);

                    if (!CHARSET_UTF_8.equalsIgnoreCase(response.getCharacterEncoding()))
                        response.setCharacterEncoding(CHARSET_UTF_8);
                    response.getWriter().print("Connection error! Check if you can connect to  the OAuth provider");

                    return "redirect:/common/sessionTimeout";
                }
                if (profile == null) {
                    // 登录失败
                    throw new RuntimeException("登录失败-获取用户信息失败");
                }

                String email = profile.getEmail();

                if (StringUtils.isBlank(email))
                    email = profile.getValidatedId();
                if (email == null) {
                    // 登录失败
                    throw new RuntimeException("登录失败-没有帐号信息");
                }

                String name = profile.getFullName();
                if (StringUtils.isBlank(name))
                    name = profile.getDisplayName();
                if (StringUtils.isBlank(name))
                    name = getName(email, profile.getFirstName(), profile.getLastName());
                if (StringUtils.isBlank(name))
                    name = profile.getProviderId() + "用户";
                String avatar = profile.getProfileImageURL();

                // profile的location存储的是oschina的个人空间地址，weibo是微博地址，github是github个人主页，google是google
                // plus地址。
                String blog = null;
                if (profile.getProviderId().equals(OpenIdManager.PROVIDER_OSCHINA)
                        || profile.getProviderId().equals(OpenIdManager.PROVIDER_WEIBO)
                        || profile.getProviderId().equals(OpenIdManager.PROVIDER_GITHUB)
                        || profile.getProviderId().equals(OpenIdManager.PROVIDER_GOOGLE))
                    blog = profile.getLocation();

                String validatedId = profile.getValidatedId();

                // 查询是否为新用户
                // profile.getProviderId() ---> type
                // email or name
                UserLoginProvider query = new UserLoginProvider();
                query.setType(profile.getProviderId());
                List<UserLoginProvider> userLoginProviders = userLoginProviderDao.findProviderListByType(query);
                // findUserListByName
                User user = systemService.getUserByLoginName(email);
                // loginName = loginName + "-" + profile.getProviderId();
                UserLoginProvider userLoginProvider = null;
                if (userLoginProviders == null || userLoginProviders.size() == 0 || user == null) {
                    user = null;
                } else {
                    for (UserLoginProvider item : userLoginProviders) {
                        if (item.getUser() != null
                                && StringUtils.equals(item.getUser().getId(), user.getId())) {
                            userLoginProvider = item;
                            break;
                        }
                    }
                    if (userLoginProvider == null) {
                        user = systemService.getUserByLoginName(email + "-" + profile.getProviderId());
                    }
                    for (UserLoginProvider item : userLoginProviders) {
                        if (item.getUser() != null
                                && StringUtils.equals(item.getUser().getId(), user.getId())) {
                            userLoginProvider = item;
                            break;
                        }
                    }
                }

                // 如果是开源中国登陆，用关联id查询，防止email变更代码丢失
                if (user == null && profile.getProviderId().equals(OpenIdManager.PROVIDER_OSCHINA)
                        && !StringUtils.isBlank(validatedId)) {
                    // profile.getProviderId() ---> type
                    // validatedId
                    query.setValidateId(validatedId);
                    userLoginProvider = userLoginProviderDao.findProviderByTypeAndValidateId(query);
                    if (userLoginProvider != null) {
                        user = systemService.getUser(userLoginProvider.getUser().getId());
                    }
                }

                if (user == null) {
                    // 新用户，将其信息插入数据库（email单一、loginName单一）
                    User newUser = new User();
                    newUser.setType(profile.getProviderId());
                    // 如果account是email则将email填进去
                    if (RegexUtils.isEmail(email))
                        newUser.setEmail(email);

                    // 默认放在根
                    newUser.setOffice(new Office("1"));
                    newUser.setName(name);
                    // 普通用户
                    newUser.setType("3");
                    newUser.setSex(1);
                    newUser.setAge("18");
                    // 通过管理员创建
                    newUser.setCreateBy(new User("1"));
                    newUser.setUpdateBy(new User("1"));
                    // 设置默认状态
                    newUser.setDelFlag("0");
                    newUser.setLoginFlag("1");
                    newUser.setStatus(0);
                    newUser.setPhoto(avatar);

                    // 登录名不可重复,进行验证
                    String loginName = email;
                    if (systemService.getUserByLoginName(loginName) != null) {
                        loginName = loginName + "-" + profile.getProviderId();
                    }
                    newUser.setLoginName(loginName);
                    // 默认密码123456,配置账号时修改账号名和密码
                    newUser.setPassword(SystemService.entryptPassword("123456"));

                    // 角色数据有效性验证，过滤不在授权内的角色
                    List<Role> roleList = Lists.newArrayList();
                    // 提供普通角色权限
                    List<String> roleIdList = Lists.newArrayList("2");
                    roleList.addAll(systemService.findAllRole().stream()
                            .filter(r -> roleIdList.contains(r.getId()))
                            .collect(Collectors.toList()));
                    newUser.setRoleList(roleList);
                    // 保存用户信息
                    systemService.saveUser(newUser);
                    logger.info("保存第三方登录用户: {}", newUser);

                    user = systemService.getUserByLoginName(loginName);

                    // 同时需要把用户保存到login provider
                    userLoginProvider = new UserLoginProvider();
                    userLoginProvider.setId(IdGen.uuid());
                    userLoginProvider.setUser(user);
                    userLoginProvider.setStatus(0);
                    userLoginProvider.setCreateDate(new Date());
                    userLoginProvider.setValidateId(profile.getValidatedId());
                    userLoginProvider.setType(profile.getProviderId());
                    userLoginProvider.setRemarks("");
                    userLoginProvider.setUrl(blog);
                    // save
                    userLoginProviderDao.insert(userLoginProvider);

                    if (profile.getProviderId().equals(OpenIdManager.PROVIDER_WEIBO))
                        try {
                            provider.updateStatus("我刚使用新浪微博登录了 #Framework#（ http://www.zeyuphoenix.com ), Spring boot开发平台,集成了我想要的开源组件.");
                        } catch (Exception e) {
                            logger.error("", e);
                        }
                    else if (profile.getProviderId().equals(OpenIdManager.PROVIDER_QQ))
                        try {
                            provider.updateStatus("我刚使用QQ登录了 #Framework#（ http://www.zeyuphoenix.com ), Spring boot开发平台,集成了我想要的开源组件.");
                        } catch (Exception e) {
                            logger.error("", e);
                        }
                    else if (profile.getProviderId().equals(OpenIdManager.PROVIDER_OSCHINA))
                        try {
                            provider.updateStatus("我刚使用OsChina登录了 #Framework#（ http://www.zeyuphoenix.com ), Spring boot开发平台,集成了我想要的开源组件.");
                        } catch (Exception e) {
                            logger.error("", e);
                        }
                } else {
                    // 已有用户
                    // 如果是开源中国登陆，更新id，刷新老数据
                    if (userLoginProvider != null && profile.getProviderId().equals(OpenIdManager.PROVIDER_OSCHINA)
                            && StringUtils.isNotBlank(validatedId)) {
                        // 根据user id 和 type 更新 login provider表
                        userLoginProvider.setValidateId(validatedId);
                        userLoginProviderDao.update(userLoginProvider);
                        logger.debug("update validated id");
                    }
                }

                // 默认密码123456
                Subject subject = SecurityUtils.getSubject();
                // 如果没有授权
                if (!subject.isAuthenticated()) {
                    // 后台模拟登录开始
                    UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginName(),
                            user.getPassword().toCharArray(), false, request.getRemoteAddr(), null, false);
                    token.setType(UsernamePasswordToken.LOGIN_TYPE_ENCRYPTION);
                    try {
                        subject.login(token);
                    } catch (Exception e) {
                        logger.error("login error: ", e);
                    }
                }

                // response.sendRedirect(request.getContextPath());
                return "redirect:/common/sessionTimeout";
            }
        } catch (Exception e) {
            logger.error("After login error: ", e);
        } finally {
            // 清除旧的cookie
            CookieUtils.setCookie(response, OpenIdManager.SOCIAL_AUTH_KEY, "", 0);
        }

        return "redirect:/common/sessionTimeout";
    }

    /**
     * //TODO
     * 测试方法,正式版本需要清除
     * 清除所有缓存和登录信息
     */
    @ResponseBody
    @RequestMapping(value = "clearCache")
    public Result clearCache() {

        UserUtils.clearCache();

        SecurityUtils.getSubject().logout();

        return new Result(Result.SUCCESS, "clear cache ok.");
    }

    // ================================================================
    // Getter & Setter
    // ================================================================

    // ================================================================
    // Private Methods
    // ================================================================

    /**
     * 获取姓名
     *
     * @param email
     *            邮箱地址
     * @param fn
     *            名
     * @param ln
     *            姓
     * @return 名称
     */
    private String getName(String email, String fn, String ln) {
        String name = "";
        // 姓名拼接
        if (StringUtils.isNotBlank(fn) && StringUtils.isNotBlank(ln)) {
            if (StringUtils.equals(fn, ln))
                name = fn;
            else {
                if (fn.length() < ln.length())
                    name = fn + ln;
                else
                    name = ln + fn;
            }
        } else {
            if (StringUtils.isNotBlank(ln))
                name = ln;
            if (StringUtils.isNotBlank(fn))
                name += fn;
        }
        // 无法获取,则去取邮箱的前缀
        if (StringUtils.isBlank(name) && RegexUtils.isEmail(email))
            name = email.substring(0, email.indexOf('@'));

        return name;
    }

    // ================================================================
    // Inner or Anonymous Class
    // ================================================================

    // ================================================================
    // Test Methods
    // ================================================================
}
